package com.violet.sharding.config;

import cn.hutool.json.JSONUtil;
import com.google.common.collect.Range;
import com.violet.common.exception.BusinessException;
import com.violet.common.util.SpringUtil;
import com.violet.sharding.properties.EnvironmentProperty;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Properties;

/**
 * 类说明: 自定义分片算发
 *
 * @author wqf
 * @date 2023/1/11 17:13
 */
@Getter
@Slf4j
public class MonthStandardShardingAlgorithm implements StandardShardingAlgorithm<Timestamp> {

    @Getter
    @Setter
    private Properties props = new Properties();

    private static final String TABLE_NAME_PREFIX = "power_dis_room_warn_record";

    @Override
    public String getType() {
        //对应yml中的type,这里怎么定义，那里就怎么写
        return "MONTH_INTERVAL";
    }

    /**
     * Sharding.
     *
     * @param availableTargetNames available data sources or table names
     * @param shardingValue        sharding value
     * @return sharding result for data source or table name
     */
    @Override
    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Timestamp> shardingValue) {
        log.info("精准分片逻辑表名称：{}，分片值：{}", JSONUtil.toJsonStr(availableTargetNames), shardingValue);
        String shardValue = String.valueOf(shardingValue.getValue()).replace("-", "").substring(0, 6);
        for (String tableName : availableTargetNames) {
            if (tableName.endsWith(shardValue)) {
                //判断表名是否存在
                List<String> allTableNameBySchema = getAllTableNameBySchema(tableName);
                if (allTableNameBySchema.size() > 0) {
                    return tableName;
                }
            }
        }
        throw new BusinessException("数据查询失败");
    }

    /**
     * Sharding.
     *
     * @param availableTargetNames available data sources or table names
     * @param shardingValue        sharding value
     * @return sharding results for data sources or table names
     */
    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Timestamp> shardingValue) {
        log.info("范围分片逻辑表名称====：{}，分片值===：{}", JSONUtil.toJsonStr(availableTargetNames), shardingValue);

        Collection<String> collect = new ArrayList<>();//返回数据库节点名称list
        Range<Timestamp> valueRange = shardingValue.getValueRange();//获取查询条件中范围值
        String slowerEndpoint = String.valueOf(valueRange.hasLowerBound() ? valueRange.lowerEndpoint() : "");//查询下限值
        String supperEndpoint = String.valueOf(valueRange.hasUpperBound() ? valueRange.upperEndpoint() : "");//查询上限值

        List<String> list = getMonthBetweenDate(slowerEndpoint, supperEndpoint);
        for (String tableName : availableTargetNames) {
            for (String l : list) {
                if (tableName.endsWith(l)) {
                    collect.add(tableName);
                }
            }
        }
        List<String> allTableNameBySchema = getAllTableNameBySchema(TABLE_NAME_PREFIX);
        collect.retainAll(allTableNameBySchema);
        return collect;
    }

    /**
     * 获取两个日期之间的所有月份 (年月)
     *
     * @param startTime
     * @param endTime
     * @return：YYYY-MM
     */
    public static List<String> getMonthBetweenDate(String startTime, String endTime) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
        // 声明保存日期集合
        List<String> list = new ArrayList<>();
        try {
            // 转化成日期类型
            Date startDate = sdf.parse(startTime);
            Date endDate = sdf.parse(endTime);

            //用Calendar 进行日期比较判断
            Calendar calendar = Calendar.getInstance();
            while (startDate.getTime() <= endDate.getTime()) {
                // 把日期添加到集合
                list.add(sdf.format(startDate).replace("-", ""));
                // 设置日期
                calendar.setTime(startDate);
                //把日期增加一天
                calendar.add(Calendar.MONTH, 1);
                // 获取增加后的日期
                startDate = calendar.getTime();
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return list;
    }


    public static List<String> getAllTableNameBySchema(String tableNamePrefix) {
        EnvironmentProperty environmentProperty = SpringUtil.getBean(EnvironmentProperty.class);
        List<String> res = new ArrayList<>();
        try (Connection connection = DriverManager.getConnection(environmentProperty.getUrl(),
                environmentProperty.getUsername(),
                environmentProperty.getPassword());
             Statement st = connection.createStatement()) {
            try (ResultSet rs = st.executeQuery("SHOW TABLES LIKE '" + tableNamePrefix + "%'")) {
                while (rs.next()) {
                    res.add(rs.getString(1));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return res;
    }

    /**
     * Initialize SPI.
     *
     * @param props properties to be initialized
     */
    @Override
    public void init(Properties props) {

    }
}
